# -*- coding: cp866 -*-
import os
import wsgiref.util
import cgi
import urlparse
import datetime
import time
import pdb
import base64
import Cookie

import MSWSGIPatterns as w
import MSSafeinsideLib as lib

c = lib.MSConfig.getConfig()
l = lib.log


class Helpers:
    @staticmethod
    def checkName(name):
        if w.checkName(name): return True
        if type(name) != type(str()): name = 'bad_type'
        return False

class MethodHandler:
    #this class should not have any fields until methods are invoked directly by http request (using getattr)
    #otherwise internal fields could be accessed too
    def __init__(self): return

    def object_registrate_form(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        t = w.PatternTable('table table-striped table-bordered table-condensed', 'eventTableSortable', 'eventTableBody')

        ret = []
        ret += ['<h2>']
        ret += ['Незарегистрированные объекты системы']
        ret += ['</h2>']
        objs_table = []
        objs_table += t.open([('Номер CID', {'class':'header'}),
                              ('Дата создания',  {'class':'blue header'}),
                              ('Последнее событие',   {'class':'yellow header'}),
                              ('Номер в системе', {'class':'green header'}),
                            ])
        hasAnyObject = False
        dirname = 'C:\\Program Files\\CarOnLine\\v4\\log\\devices\\messages\\' + datetime.datetime.now().strftime('%Y.%m.%d') + '\\-1\\notregister_fail\\'
        reader = lib.MSDBReader(c.get('msdb','host'),
                                c.get('msdb','port'),
                                c.get('msdb','dbname'),
                                c.get('msdb','user'),
                                c.get('msdb','pwd'))
        reader.connect()
        cursor = reader.connection.cursor()
        if os.path.isdir(dirname):
            for i in os.listdir(dirname):
                hasAnyObject = True
                dpath = dirname + i
                stat = os.stat(dpath)
                mtime = datetime.datetime.fromtimestamp(stat.st_mtime)
                ctime = datetime.datetime.fromtimestamp(stat.st_ctime)
                oid = "не зарегистрирован"
                query = "SELECT uid,id FROM _devices_internal WHERE uid='%s'" % str(i)
                cursor.execute(query)
                res = cursor.fetchall()
                if len(res) > 0 and len(res[0]) > 0:
                    query = "SELECT login,defaultDevice FROM _users_internal WHERE defaultDevice='%s'" % (str(res[0][1]))
                    cursor.execute(query)
                    res = cursor.fetchall()
                    if len(res) > 0 and len(res[0]) > 0:
                        oid = "%s(%s)" % (res[0][0], res[0][1])
                objs_table += t.add(( str(i), ctime.strftime('%Y-%m-%dT%H:%M'), mtime.strftime('%Y-%m-%dT%H:%M'), oid ))
        objs_table += t.close()
        if hasAnyObject:
            ret += objs_table
        else:
            ret += ['Незарегистрированных объектов не найдено']
        ret += ['<form action="/method/object_registrate" method="post">']
        ret += ['<fieldset>']
        ret += ['<legend>Регистрация нового устройства</legend>']
        ret += ['<div class="clearfix">']
        ret += ['<label for="obj_id">Идентификатор устройства</label>']
        ret += ['<div class="input">']
        ret += ['<input class="span8" id="obj_id" name="obj_id" size="30" type="text">']
        ret += ['<span class="help-inline">4 и более цифр</span>']
        ret += ['</div>']
        ret += ['</div>']
        ret += ['<div class="clearfix">']
        ret += ['<label for="obj_mac">MAC устройства</label>']
        ret += ['<div class="input">']
        ret += ['<input class="span8" id="obj_mac" name="obj_mac" size="30" type="text">']
        ret += ['</div>']
        ret += ['</div>']
        ret += ['</fieldset>']
        ret += ['<button class="btn primary" type="submit">Зарегистрировать</button>']
        ret += ['</form>']

        return ret

    def object_delete(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        old = form.getvalue('obj_id')
        if not Helpers.checkName(old): return ['Ошибка - недопустимый идентификатор']
        old = 'dev' + str(old)
        new = 'del' + str(old)
        
        reader = lib.MSDBReader(c.get('msdb','host'),
                                c.get('msdb','port'),
                                c.get('msdb','dbname'),
                                c.get('msdb','user'),
                                c.get('msdb','pwd'))
        reader.connect()
        if lib.MSObjects.renameObject(reader.connection, old, new):
            return ['Устройство успешно удалено - переименовано из "%s" в "%s"' % (old, new)]
        return ['Устройство не переименовано - проверьте имя']        
        
    def object_rename(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        old = form.getvalue('obj_oldid')
        if not Helpers.checkName(old): return ['Ошибка - недопустимый идентификатор']
        new = form.getvalue('obj_newid')
        if not Helpers.checkName(new): return ['Ошибка - недопустимый новый идентификатор']
        
        old = 'dev' + str(old)
        new = 'dev' + str(new)

        reader = lib.MSDBReader(c.get('msdb','host'),
                                c.get('msdb','port'),
                                c.get('msdb','dbname'),
                                c.get('msdb','user'),
                                c.get('msdb','pwd'))
        reader.connect()
        if lib.MSObjects.renameObject(reader.connection, old, new):
            return ['Устройство успешно переименовано из "%s" в "%s"' % (old, new)]
        return ['Устройство не переименовано - проверьте имя']
        
    def object_registrate(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        oid = form.getvalue('obj_id')
        if not Helpers.checkName(oid): return ['Ошибка - недопустимый идентификатор']
        mac = form.getvalue('obj_mac')
        if not Helpers.checkName(mac): return ['Ошибка - недопустимый MAC']

        reader = lib.MSDBReader(c.get('msdb','host'),
                                c.get('msdb','port'),
                                c.get('msdb','dbname'),
                                c.get('msdb','user'),
                                c.get('msdb','pwd'))
        reader.connect()
        try:
            res = lib.MSObjects.registrateObjectInMSDB(reader.connection, 'C:\\Magic\\ms.online.normal_objects.cfg', oid, mac)
        except Exception,err:
            return ['Ошибка - не удалось зарегистрировать устройство: ' + str(err)]
        return ['Устройство успешно зарегистрировано: ' + str(res)]

    def object_reload_firmware(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        oid = form.getvalue('obj_oid')
        return ['Функция временно не доступна']
        if not len(oid): return ['Внутренняя ошибка']
        reader = lib.MSDBReader(c.get('msdb','host'),
                                c.get('msdb','port'),
                                c.get('msdb','dbname'),
                                c.get('msdb','user'),
                                c.get('msdb','pwd'))

        dbprefix = '[%s].[dbo]' % c.get('msdb','dbname')
        reader.connect()
        deviceId = str(oid)
        gps = "127,107,106,105,104,103,102,101,100,97,96,89,88,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,59,40,26"
        gsm = "127,108,107,106,105,104,103,102,101,100,97,96,89,88,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,59,48,47,46,45,40,26"
        balance = "127,120,110,108,107,106,105,104,103,102,101,100,98,97,96,95,94,93,92,91,89,88,87,86,85,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,40,39,35,34,33,32,31,30,29,28,27,26,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1"
        brk = "120,110,108,107,106,105,104,103,102,101,100,98,97,96,95,94,93,92,91,89,88,87,86,85,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,57,56,55,54,53,52,51,50,49,44,43,40,35,34,33,32,31,26,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1"
        cam1 = "127,127,127,127,127,127,127,127,126,126,126,126,126,126,126,126,125,125,125,125,125,125,125,125,124,124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,122,122,122,122,122,122,122,122,121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,110,110,110,110,110,110,110,110,108,108,108,108,108,108,108,108,107,107,107,107,107,107,107,107,106,106,106,106,106,106,106,106,105,105,105,105,105,105,105,105,104,104,104,104,104,104,104,104,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,99,99,99,99,99,99,99,99,98,98,98,98,98,98,98,98,97,97,97,97,97,97,97,97,96,96,96,96,96,96,96,96,95,95,95,95,95,95,95,95,94,94,94,94,94,94,94,94,93,93,93,93,93,93,93,93,92,92,92,92,92,92,92,92,91,91,91,91,91,91,91,91,90,90,90,90,90,90,90,90,88,88,88,88,88,88,88,88,"
        cam2 = "127,127,127,127,127,127,127,127,126,126,126,126,126,126,126,126,125,125,125,125,125,125,125,125,124,124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,122,122,122,122,122,122,122,122,121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,110,110,110,110,110,110,110,110,108,108,108,108,108,108,108,108,107,107,107,107,107,107,107,107,106,106,106,106,106,106,106,106,105,105,105,105,105,105,105,105,104,104,104,104,104,104,104,104,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,99,99,99,99,99,99,99,99,98,98,98,98,98,98,98,98,97,97,97,97,97,97,97,97,96,96,96,96,96,96,96,96,95,95,95,95,95,95,95,95,94,94,94,94,94,94,94,94,93,93,93,93,93,93,93,93,92,92,92,92,92,92,92,92,91,91,91,91,91,91,91,91,90,90,90,90,90,90,90,90,88,88,88,88,88,88,88,88,"
        cam3 = "127,127,127,127,127,127,127,127,126,126,126,126,126,126,126,126,125,125,125,125,125,125,125,125,124,124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,122,122,122,122,122,122,122,122,121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,110,110,110,110,110,110,110,110,108,108,108,108,108,108,108,108,107,107,107,107,107,107,107,107,106,106,106,106,106,106,106,106,105,105,105,105,105,105,105,105,104,104,104,104,104,104,104,104,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,99,99,99,99,99,99,99,99,98,98,98,98,98,98,98,98,97,97,97,97,97,97,97,97,96,96,96,96,96,96,96,96,95,95,95,95,95,95,95,95,94,94,94,94,94,94,94,94,93,93,93,93,93,93,93,93,92,92,92,92,92,92,92,92,91,91,91,91,91,91,91,91,90,90,90,90,90,90,90,90,88,88,88,88,88,88,88,88,"
        cam4 = "127,127,127,127,127,127,127,127,126,126,126,126,126,126,126,126,125,125,125,125,125,125,125,125,124,124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,122,122,122,122,122,122,122,122,121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,110,110,110,110,110,110,110,110,108,108,108,108,108,108,108,108,107,107,107,107,107,107,107,107,106,106,106,106,106,106,106,106,105,105,105,105,105,105,105,105,104,104,104,104,104,104,104,104,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,99,99,99,99,99,99,99,99,98,98,98,98,98,98,98,98,97,97,97,97,97,97,97,97,96,96,96,96,96,96,96,96,95,95,95,95,95,95,95,95,94,94,94,94,94,94,94,94,93,93,93,93,93,93,93,93,92,92,92,92,92,92,92,92,91,91,91,91,91,91,91,91,90,90,90,90,90,90,90,90,88,88,88,88,88,88,88,88,"
        cam5 = "127,127,127,127,127,127,127,127,126,126,126,126,126,126,126,126,125,125,125,125,125,125,125,125,124,124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,122,122,122,122,122,122,122,122,121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,110,110,110,110,110,110,110,110,108,108,108,108,108,108,108,108,107,107,107,107,107,107,107,107,106,106,106,106,106,106,106,106,105,105,105,105,105,105,105,105,104,104,104,104,104,104,104,104,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,99,99,99,99,99,99,99,99,98,98,98,98,98,98,98,98,97,97,97,97,97,97,97,97,96,96,96,96,96,96,96,96,95,95,95,95,95,95,95,95,94,94,94,94,94,94,94,94,93,93,93,93,93,93,93,93,92,92,92,92,92,92,92,92,91,91,91,91,91,91,91,91,90,90,90,90,90,90,90,90,88,88,88,88,88,88,88,88,"
        cam6 = "127,127,127,127,127,127,127,127,126,126,126,126,126,126,126,126,125,125,125,125,125,125,125,125,124,124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,122,122,122,122,122,122,122,122,121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,110,110,110,110,110,110,110,110,108,108,108,108,108,108,108,108,107,107,107,107,107,107,107,107,106,106,106,106,106,106,106,106,105,105,105,105,105,105,105,105,104,104,104,104,104,104,104,104,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,99,99,99,99,99,99,99,99,98,98,98,98,98,98,98,98,97,97,97,97,97,97,97,97,96,96,96,96,96,96,96,96,95,95,95,95,95,95,95,95,94,94,94,94,94,94,94,94,93,93,93,93,93,93,93,93,92,92,92,92,92,92,92,92,91,91,91,91,91,91,91,91,90,90,90,90,90,90,90,90,88,88,88,88,88,88,88,88,"
        cam7 = "127,127,127,127,127,127,127,127,126,126,126,126,126,126,126,126,125,125,125,125,125,125,125,125,124,124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,122,122,122,122,122,122,122,122,121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,110,110,110,110,110,110,110,110,108,108,108,108,108,108,108,108,107,107,107,107,107,107,107,107,106,106,106,106,106,106,106,106,105,105,105,105,105,105,105,105,104,104,104,104,104,104,104,104,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,99,99,99,99,99,99,99,99,98,98,98,98,98,98,98,98,97,97,97,97,97,97,97,97,96,96,96,96,96,96,96,96,95,95,95,95,95,95,95,95,94,94,94,94,94,94,94,94,93,93,93,93,93,93,93,93,92,92,92,92,92,92,92,92,91,91,91,91,91,91,91,91,90,90,90,90,90,90,90,90,88,88,88,88,88,88,88,88,"
        cam8 = "127,127,127,127,127,127,127,127,126,126,126,126,126,126,126,126,125,125,125,125,125,125,125,125,124,124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,122,122,122,122,122,122,122,122,121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,110,110,110,110,110,110,110,110,108,108,108,108,108,108,108,108,107,107,107,107,107,107,107,107,106,106,106,106,106,106,106,106,105,105,105,105,105,105,105,105,104,104,104,104,104,104,104,104,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,99,99,99,99,99,99,99,99,98,98,98,98,98,98,98,98,97,97,97,97,97,97,97,97,96,96,96,96,96,96,96,96,95,95,95,95,95,95,95,95,94,94,94,94,94,94,94,94,93,93,93,93,93,93,93,93,92,92,92,92,92,92,92,92,91,91,91,91,91,91,91,91,90,90,90,90,90,90,90,90,88,88,88,88,88,88,88,88,"
        gpsOff = "False"
        gsmOff = "False"
        brkOff = "False"
        balanceOff = "False"
        changeProgramMemory = "False"
        changeProgramGsm = "True"
        nameProgramFilePgsm = "CID_1222A.bin"
        levelOff = "99" 
        levelBuff = "108,107,106,105,104,103,102,101,100,97,96,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,40,36,33,32,31,26"
        levelActivTCP ="94"
        levelActivGPRS = "91,90,89,88,87,86,48,47,46,45,39,35,34,29,27,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5"
        levelDeactivGPRS = "127,126,125,124,123,122,121,120,115,114,113,112,111,110,98,95,93,92,85,84,58,57,56,55,54,53,52,51,50,49,44,43,41,38,37,30,28,25,24,4,3,2,1"
        levelDeactivGPRSRoam = "42"
        changeVoice = "False"
        voiceFile = "voice.bin"

        queryPrefix = "INSERT INTO %s.[_device_transfer] (\"deviceId\", \"gps\", \"gsm\", \"balance\", \"brk\", \"cam1\", \"cam2\", \"cam3\", \"cam4\", \"cam5\", \"cam6\", \"cam7\", \"cam8\", \"gpsOff\", \"gsmOff\", \"brkOff\", \"balanceOff\", \"changeProgramMemory\", \"changeProgramGsm\", \"nameProgramFilePgsm\", \"levelOff\", \"levelBuff\", \"levelActivTCP\", \"levelActivGPRS\", \"levelDeactivGPRS\", \"levelDeactivGPRSRoam\", \"changeVoice\", \"voiceFile\") VALUES " % dbprefix
        querySuffix = "('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')" % (deviceId, gps, gsm, balance, brk, cam1, cam2, cam3, cam4, cam5, cam6, cam7, cam8, gpsOff, gsmOff, brkOff, balanceOff, changeProgramMemory, changeProgramGsm, nameProgramFilePgsm, levelOff, levelBuff, levelActivTCP, levelActivGPRS, levelDeactivGPRS, levelDeactivGPRSRoam, changeVoice, voiceFile)
        query = queryPrefix + querySuffix
        cursor = reader.connection.cursor()
        cursor.execute(query)
        reader.connection.commit()
        return ['Перепрошивка успешно инициирована (версия CID_1222A)']

    def object_make_test(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        oid = form.getvalue('obj_oid')
        if not len(oid): return ['Внутренняя ошибка']
        lib.MSObjects.makeObject_TEST('C:\\Magic\\ms.online.normal_objects.cfg', oid)
        return ['Объект ' + oid + ' переведен в тестовые']

    def object_make_normal(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        oid = form.getvalue('obj_oid')
        if not len(oid): return ['Внутренняя ошибка']
        lib.MSObjects.makeObject_NORMAL('C:\\Magic\\ms.online.normal_objects.cfg', oid)
        return ['Объект ' + oid + ' переведен в нормальные']

    def object_list_fast(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        mode = form.getvalue('mode')
        if not Helpers.checkName(mode) or (mode != 'admin' and mode != 'test'): mode = 'normal'

        reader = lib.MSDBReader(c.get('msdb','host'),
                                c.get('msdb','port'),
                                c.get('msdb','dbname'),
                                c.get('msdb','user'),
                                c.get('msdb','pwd'))
        reader.connect()
        readerapi = lib.MSAPIReader(c.get('msapi','url'))

        objects = {}
        lib.MSObjects.readObjectsFromMSDB(reader.connection.cursor(), objects, 'dev')
        lib.MSObjects.readObjectsFromMSDB(reader.connection.cursor(), objects, 'fast')
        lib.MSObjects.readObjectsFromFile(c.get('msapi','objects_cfg'), objects)
        allObjects = lib.MSObjects.filterObjectsAs_NORMAL_TEST(objects, 'C:\\Magic\\ms.online.normal_objects.cfg')
        if mode == 'test': objects = allObjects[0]
        if mode == 'normal': objects = allObjects[1]
        if mode == 'admin':
            objects = {}
            objects.update(allObjects[0])
            objects.update(allObjects[1])

        ret = []

        ret += ['<form action="/method/object_list" method="post">']
        ret += ['<input type="hidden" name="mode" value="normal">']
        ret += ['<button class="btn primary" type="submit">Расширенная информация об объектах</button></form>']

        ret += ['<h2>']
        ret += ['Объекты системы']
        ret += ['</h2>']

        ret += ['<form action="/method/object_list_fast" method="post">']
        ret += ['<input type="hidden" name="mode" value="normal">']
        ret += ['<button class="btn primary" type="submit">Нормальные объекты</button></form>']
        ret += ['<form action="/method/object_list_fast" method="post">']
        ret += ['<input type="hidden" name="mode" value="test">']
        ret += ['<button class="btn primary" type="submit">Тестовые объекты</button></form>']
        ret += ['<form action="/method/object_list_fast" method="post">']
        ret += ['<input type="hidden" name="mode" value="admin">']
        ret += ['<button class="btn primary" type="submit">Режим администрирования</button></form>']

        t = w.PatternTable('table table-striped table-bordered table-condensed', 'eventTableSortable', 'eventTableBody')

        objs_table = []

        objs_table += t.open([('pcn id', {'class':'header'}),
                       ('ms id',  {'class':'yellow header'}),
                       ('min ago',   {'class':'yellow header'}),
                       ('event type',   {'class':'green header'}),
                       ('version',   {'class':'green header'}),
                       ('admin',   {'class':'blue header'}),
                      ])
        num = 0
        ctr_online = 0
        ctr_offline = 0
        cursor = reader.connection.cursor()
        for k,o in objects.iteritems():
            try:
                status = 'OFFLINE'
                if o.objectMSId != 0:
                    query = 'SELECT TOP(1) deviceId,eventTime,type,id FROM _event_data_internal WHERE deviceId=%s ORDER BY eventTime DESC' % (str(o.objectMSId))
                    cursor.execute(query)
                    buff = cursor.fetchall()
                    try:
                        ctr_offline += 1
                        td = datetime.datetime.fromtimestamp(time.time()) - buff[0][1]
                        if td < datetime.timedelta(seconds=3600*24):
                            status = str(td).split('.')[0]
                            ctr_online += 1
                            ctr_offline -= 1
                    except Exception, err:
                        l.warning('MSMethods::object_list: db obj(%s:%s) can\'t parse last event: %s', str(o.objectMSId), str(o.objectPCNId), str(err))
                if len(o.objectSKey) > 0:
                    o.lastEventType = 'N/A'
                    o.versionSoftPGSM = 'N/A'
                    try:
                        ocompat = { o.objectSKey: o }
                        ids, events, lastEventId = readerapi.getEventsPGSM(ocompat, 0)
                        maxEvent = None
                        for e in events:
                            if not maxEvent: maxEvent = e
                            if e.eventGenerateTime > maxEvent.eventGenerateTime: maxEvent = e
                        e = maxEvent
                        o.lastEventType = e.eventType
                        ctr_offline += 1
                        td = datetime.datetime.fromtimestamp(time.time()) - e.eventGenerateTime
                        if td < datetime.timedelta(seconds=3600*24):
                            status = str(td).split('.')[0]
                            ctr_online += 1
                            ctr_offline -= 1
                    except Exception, err:
                        l.warning('MSMethods::object_list: api obj(%s:%s) can\'t parse last event: %s', str(o.objectMSId), str(o.objectPCNId), str(err))

                buttons = ''
                if mode == 'admin':
                    if o.isTestObject:
                        buttons = '<form action="/method/object_make_normal" method="post"><input type="hidden" name="obj_oid" value="%s"><button class="btn" type="submit">сделать нормальным</button></form>' % (str(o.objectPCNId))
                    else:
                        buttons = '<form action="/method/object_make_test" method="post"><input type="hidden" name="obj_oid" value="%s"><button class="btn" type="submit">сделать тестовым</button></form>' % (str(o.objectPCNId))
                    buttons += '<form action="/method/object_reload_firmware" method="post"><input type="hidden" name="obj_oid" value="%s"><button class="btn" type="submit">перепрошить</button></form>' % (str(o.objectMSId))

                objs_table += t.add((  str(o.objectPCNId), 
                                str(o.objectMSId),
                                status,
                                str(o.lastEventType), 
                                str(o.versionSoftPGSM), 
                                buttons ))
            except Exception, err:
                l.error('MSMethods::object_list: obj(%s:%s): %s', str(o.objectMSId), str(o.objectPCNId), str(err))
            num += 1
        objs_table += t.close()

        ret += ['Всего объектов: <div class="eventTableSize">%d/%d</div><br>' % (num, len(objects))]
        ret += ['Объектов онлайн: %d<br>' % (ctr_online)]
        ret += ['Объектов офлайн: %d<br>' % (ctr_offline)]
        ret += objs_table

        ret += ['Время обновления: ']
        ret += ['<div class="eventTableUpdTime">' + (datetime.datetime.fromtimestamp(time.time())).strftime(lib.MSDatetime.FormatMagicDB) + '</div><br>']
        return ret

    def bad_object_list_form(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        ret = []
        ret += ['<form action="/method/bad_object_list" method="post">']
        ret += ['<input name="mode" value="normal">']
        ret += ['<input name="start" value="%s">' % datetime.datetime.today().date().strftime('%Y%m%d')]
        ret += ['<input name="finish" value="%s">' % ((datetime.datetime.today() + datetime.timedelta(days=1)).date().strftime('%Y%m%d'))]
        ret += ['<button class="btn primary" type="submit">Получить информацию о событиях</button></form>']
        return ret

    def bad_object_list(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        mode = form.getvalue('mode')
        if not Helpers.checkName(mode) or (mode != 'admin' and mode != 'test'): mode = 'normal'
        start = form.getvalue('start')
        finish = form.getvalue('finish')

        reader = lib.MSDBReader(c.get('msdb','host'),
                                c.get('msdb','port'),
                                c.get('msdb','dbname'),
                                c.get('msdb','user'),
                                c.get('msdb','pwd'))
        reader.connect()
        readerapi = lib.MSAPIReader(c.get('msapi','url'))

        objects = {}
        lib.MSObjects.readObjectsFromMSDB(reader.connection.cursor(), objects, 'dev')
        lib.MSObjects.readObjectsFromMSDB(reader.connection.cursor(), objects, 'fast')
        lib.MSObjects.readObjectsFromFile(c.get('msapi','objects_cfg'), objects)
        allObjects = lib.MSObjects.filterObjectsAs_NORMAL_TEST(objects, 'C:\\Magic\\ms.online.normal_objects.cfg')
        if mode == 'test': objects = allObjects[0]
        if mode == 'normal': objects = allObjects[1]
        if mode == 'admin':
            objects = {}
            objects.update(allObjects[0])
            objects.update(allObjects[1])

        ret = []

        t = w.PatternTable('table table-striped table-bordered table-condensed', 'eventTableSortable', 'eventTableBody')

        objs_table = []

        objs_table += t.open([('pcn id', {'class':'header'}),
                       ('ms id',  {'class':'yellow header'}),
                       ('events',   {'class':'blue header'}),
                      ])
        num = 0
        ctr_online = 0
        ctr_offline = 0
        cursor = reader.connection.cursor()
        for k,o in objects.iteritems():
            try:
                query = 'exec [TransferTest].[dbo].[device_GetCountEventFromType] %s' % (str(o.objectMSId))
                if start != None and finish != None:
                    query += ", '%s', '%s'" % (start, finish)
                cursor.execute(query)
                buff = cursor.fetchall()
                gg = ''
                for dr in buff:
                    gg+='<td>Event '+str(dr[1])+': '+str(dr[2])+'</td>';

                objs_table += t.add((  str(o.objectPCNId), 
                                str(o.objectMSId),
                                gg))
            except Exception, err:
                l.error('MSMethods::object_list: obj(%s:%s): %s', str(o.objectMSId), str(o.objectPCNId), str(err))
            num += 1
        objs_table += t.close()

        ret += ['Всего объектов: <div class="eventTableSize">%d/%d</div><br>' % (num, len(objects))]
        ret += ['<b>Event 0: transmit event more than 3 min<br>']
        ret += ['Event 33: switch between GSM<br>']
        ret += ['Event 61: lost GPRS<br>']
        ret += ['Event 72: no connection to server<br>']
        ret += ['Event 82: switch from 1 to 2 sim<br></b>']
        ret += objs_table

        ret += ['Время обновления: ']
        ret += ['<div class="eventTableUpdTime">' + (datetime.datetime.fromtimestamp(time.time())).strftime(lib.MSDatetime.FormatMagicDB) + '</div><br>']
        return ret



    def object_list(self, environ, start_response):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        mode = form.getvalue('mode')
        if not Helpers.checkName(mode) or (mode != 'admin' and mode != 'test'): mode = 'normal'

        reader = lib.MSDBReader(c.get('msdb','host'),
                                c.get('msdb','port'),
                                c.get('msdb','dbname'),
                                c.get('msdb','user'),
                                c.get('msdb','pwd'))
        reader.connect()
        readerapi = lib.MSAPIReader(c.get('msapi','url'))

        objects = {}
        lib.MSObjects.readObjectsFromMSDB(reader.connection.cursor(), objects, 'dev')
        lib.MSObjects.readObjectsFromMSDB(reader.connection.cursor(), objects, 'fast')
        lib.MSObjects.readObjectsFromFile(c.get('msapi','objects_cfg'), objects)
        allObjects = lib.MSObjects.filterObjectsAs_NORMAL_TEST(objects, 'C:\\Magic\\ms.online.normal_objects.cfg')
        if mode == 'test': objects = allObjects[0]
        if mode == 'normal': objects = allObjects[1]
        if mode == 'admin':
            objects = {}
            objects.update(allObjects[0])
            objects.update(allObjects[1])

        ret = []

        ret += ['<form action="/method/object_list_fast" method="post">']
        ret += ['<input type="hidden" name="mode" value="normal">']
        ret += ['<button class="btn primary" type="submit">Быстрая информация об объектах</button></form>']

        ret += ['<h2>']
        ret += ['Объекты системы']
        ret += ['</h2>']

        ret += ['<form action="/method/object_list" method="post">']
        ret += ['<input type="hidden" name="mode" value="normal">']
        ret += ['<button class="btn primary" type="submit">Нормальные объекты</button></form>']
        ret += ['<form action="/method/object_list" method="post">']
        ret += ['<input type="hidden" name="mode" value="test">']
        ret += ['<button class="btn primary" type="submit">Тестовые объекты</button></form>']
        ret += ['<form action="/method/object_list" method="post">']
        ret += ['<input type="hidden" name="mode" value="admin">']
        ret += ['<button class="btn primary" type="submit">Режим администрирования</button></form>']

        t = w.PatternTable('table table-striped table-bordered table-condensed', 'eventTableSortable', 'eventTableBody')

        objs_table = []

        objs_table += t.open([('pcn id', {'class':'header'}),
                       ('ms id',  {'class':'yellow header'}),
                       ('min ago',   {'class':'yellow header'}),
                       ('event type',   {'class':'green header'}),
                       ('signal',   {'class':'blue header'}),
                       ('version',   {'class':'green header'}),
                       ('operator',   {'class':'blue header'}),
                       ('sim/ip',  {'class':'yellow header'}),
                       ('comment',   {'class':'yellow header'}),
                       ('admin',   {'class':'blue header'}),
                      ])
        num = 0
        ctr_online = 0
        ctr_offline = 0
        cursor = reader.connection.cursor()
        for k,o in objects.iteritems():
            try:
                status = 'OFFLINE'
                signal = 'N/A'
                operator = 'N/A'

                if o.objectMSId != 0:
                    query = 'SELECT TOP(1) deviceId,eventTime,type,id FROM _event_data_internal WHERE deviceId=%s ORDER BY eventTime DESC' % (str(o.objectMSId))
                    cursor.execute(query)
                    buff = cursor.fetchall()
                    try:
                        ctr_offline += 1
                        td = datetime.datetime.fromtimestamp(time.time()) - buff[0][1]
                        if td < datetime.timedelta(seconds=3600*24):
                            status = str(td).split('.')[0]
                            ctr_online += 1
                            ctr_offline -= 1
                    except Exception, err:
                        l.warning('MSMethods::object_list: db obj(%s:%s) can\'t parse last event: %s', str(o.objectMSId), str(o.objectPCNId), str(err))

                    query = 'SELECT TOP(1) deviceId,eventTime,db,nc FROM _gsm_data_internal WHERE deviceId=%s ORDER BY eventTime DESC' % (str(o.objectMSId))
                    cursor.execute(query)
                    buff = cursor.fetchall()
                    try:
                        signal = str(buff[0][2])
                        opid = int(buff[0][3])
                        operator = {1:'MTS', 2:'Megafon'}.get(opid, str(opid))
                    except Exception, err:
                        l.warning('MSMethods::object_list: obj(%s:%s) can\'t parse extra info: %s', str(o.objectMSId), str(o.objectPCNId), str(err))

                    query = 'SELECT TOP(1) deviceId,changeProgramGsm FROM _device_transfer WHERE deviceId=%s ORDER BY id DESC' % (str(o.objectMSId))
                    cursor.execute(query)
                    buff = cursor.fetchall()
                    try:
                        st = buff[0][1]
                        if st == 'False' or st == False:
                            status = status + '/ПЕРЕПРОШИВАЕТСЯ'
                    except Exception, err:
                        l.warning('MSMethods::object_list: db obj(%s:%s) can\'t parse firmware status: %s', str(o.objectMSId), str(o.objectPCNId), str(err))

                if len(o.objectSKey) > 0:
                    o.lastEventType = 'N/A'
                    o.versionSoftPGSM = 'N/A'
                    try:
                        ocompat = { o.objectSKey: o }
                        ids, events, lastEventId = readerapi.getEventsPGSM(ocompat, 0)
                        maxEvent = None
                        for e in events:
                            if not maxEvent: maxEvent = e
                            if e.eventGenerateTime > maxEvent.eventGenerateTime: maxEvent = e
                        e = maxEvent
                        o.lastEventType = e.eventType
                        ctr_offline += 1
                        td = datetime.datetime.fromtimestamp(time.time()) - e.eventGenerateTime
                        if td < datetime.timedelta(seconds=3600*24):
                            status = str(td).split('.')[0]
                            ctr_online += 1
                            ctr_offline -= 1
                    except Exception, err:
                        l.warning('MSMethods::object_list: api obj(%s:%s) can\'t parse last event: %s', str(o.objectMSId), str(o.objectPCNId), str(err))

                buttons = ''
                if mode == 'admin':
                    if o.isTestObject:
                        buttons = '<form action="/method/object_make_normal" method="post"><input type="hidden" name="obj_oid" value="%s"><button class="btn" type="submit">сделать нормальным</button></form>' % (str(o.objectPCNId))
                    else:
                        buttons = '<form action="/method/object_make_test" method="post"><input type="hidden" name="obj_oid" value="%s"><button class="btn" type="submit">сделать тестовым</button></form>' % (str(o.objectPCNId))
                    buttons += '<form action="/method/object_reload_firmware" method="post"><input type="hidden" name="obj_oid" value="%s"><button class="btn" type="submit">перепрошить</button></form>' % (str(o.objectMSId))

                objs_table += t.add((  str(o.objectPCNId), 
                                str(o.objectMSId),
                                status,
                                str(o.lastEventType), 
                                signal, 
                                str(o.versionSoftPGSM), 
                                operator,
                                'N/A', 
                                'N/A', 
                                buttons ))
            except Exception, err:
                l.error('MSMethods::object_list: obj(%s:%s): %s', str(o.objectMSId), str(o.objectPCNId), str(err))
            num += 1
        objs_table += t.close()

        ret += ['Всего объектов: <div class="eventTableSize">%d/%d</div><br>' % (num, len(objects))]
        ret += ['Объектов онлайн: %d<br>' % (ctr_online)]
        ret += ['Объектов офлайн: %d<br>' % (ctr_offline)]
        ret += objs_table

        ret += ['Время обновления: ']
        ret += ['<div class="eventTableUpdTime">' + (datetime.datetime.fromtimestamp(time.time())).strftime(lib.MSDatetime.FormatMagicDB) + '</div><br>']
        return ret

        return ['OK SNAFU']

    def login(self, environ, start_response, cookie):
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        email   = form.getvalue('login')
        if not Helpers.checkName(email):
            w.Common.putHeader(start_response, 200, [('html', 'text')])
            return ['failed']
        pwd     = form.getvalue('password')
        if not Helpers.checkName(pwd): 
            w.Common.putHeader(start_response, 200, [('html', 'text')])
            return ['failed']

        uid         = MSUser.hash(email)
        user        = MSDatabase.getInstance().users.find(uid)
        ret         = ['Выполнен вход %s' % (email)]
        ret         += ['<form action="method/user_card" method="post">']
        ret         += ['<button class="btn primary" type="submit">Продолжить</button></form>']
        if user == None:
            reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                       c.get('msdb','port'),
                                       c.get('msdb','dbname'),
                                       c.get('msdb','user'),
                                       c.get('msdb','pwd'))
            reader.connect()
            cursor = reader.connection.cursor()
            query = "SELECT login,password,defaultDevice FROM _users_internal WHERE login='%s'" % (email)
            cursor.execute(query)
            res = cursor.fetchall()
            if len(res) != 0 and res[0][1] == pwd:
                user = MSUser.MSUser(c.get('main','dbpath'), uid)
                user.open()
                user.storage.meta['email'] = email
                user.storage.meta['pwd'] = MSUser.hash(pwd)
                user.storage.meta['msId'] = str(res[0][2])
                user.flush()
                MSDatabase.getInstance().users.add(user)
                ret = ['Выполнен вход %s<br>' % (email)]
                ret += ['Добро пожаловать в AN-Direct!<br>']
                ret += ['Необходимо заполнить информацию по устройству<br>']
                ret += ['<form action="/method/user_card_modify" method="post">']
                ret += ['<button class="btn primary" type="submit">Продолжить</button></form>']

        if user != None and (MSUser.hash(pwd) == user.storage.meta['pwd']):
            cookie = user.getCookie()
            log.info('logged in: %s,%s', email, cookie.output(header=''))
            w.Common.putHeader(start_response, 200, [('html', 'text'), ('Set-Cookie', cookie.output(header=''))])
            return ret

        w.Common.putHeader(start_response, 200, [('html', 'text')])
        return ['failed']

    def logoff(self, environ, start_response, cookie):
        cookie = MSUser.cookie(environ)
        result = MSUser.Cookie.SimpleCookie()
        if MSUser.isValid(cookie):
            if cookie.has_key('id'):
                result['id'] = str(cookie['id'].value)
                result['id']['expires'] = -1
            if cookie.has_key('session'):
                result['session'] = str(cookie['session'].value)
                result['session']['expires'] = -1
            log.info('logged off: was (%s) set(%s)', cookie.output(header=''), result.output(header=''))
        w.Common.putHeader(start_response, 200, [('html', 'text'), ('Set-Cookie', result.output(header=''))])
        return ['logged off']

    def register(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])

        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        email   = form.getvalue('user_email')
        if not Helpers.checkName(email): return ['Ошибка - недопустимый email']
        pwd     = form.getvalue('user_pwd')
        if not Helpers.checkName(pwd): return ['Ошибка - недопустимый пароль']
        pwd2    = form.getvalue('user_pwd2')
        if not Helpers.checkName(pwd2): return ['Ошибка - недопустимый пароль']
        if MSUser.hash(pwd) != MSUser.hash(pwd2): return ['Ошибка - пароли несовпадают']

        cardtype    = form.getvalue('user_cardtype')
        realname    = form.getvalue('user_realname')
        phone       = form.getvalue('user_phone')
        civility    = form.getvalue('user_civility')
        startdate   = form.getvalue('user_startdate')
        stopdate    = form.getvalue('user_stopdate')
        photolink   = form.getvalue('user_photolink')
        extrainfo   = form.getvalue('user_extrainfo')

        if (not startdate) or (not stopdate): return ['Ошибка - введите дату выдачи и оконачания карты Hot-Line в требуемом формате']
        try:
            if (not startdate) or (not stopdate): raise RuntimeError('argument missing')
            startdate = datetime.datetime.strptime(startdate, '%d.%m.%Y')
            stopdate  = datetime.datetime.strptime(stopdate, '%d.%m.%Y')
        except:
            return ['Ошибка - введите дату выдачи и оконачания карты Hot-Line в требуемом формате']

        u = MSUser.MSUser(c.get('main','dbpath'), MSUser.hash(email))
        u.open()
        if u.storage.txn > 0: return ['failed - already exists']
        u.storage.meta['email'] = email
        u.storage.meta['pwd'] = MSUser.hash(pwd)
        if cardtype:    u.storage.meta['cardtype'] = cardtype
        if realname:    u.storage.meta['realname'] = realname
        if phone:       u.storage.meta['phone'] = phone
        if civility:    u.storage.meta['civility'] = civility
        if startdate:   u.storage.meta['startdate'] = startdate
        if stopdate:    u.storage.meta['stopdate'] = stopdate
        if photolink:   u.storage.meta['photolink'] = photolink
        if extrainfo:   u.storage.meta['extrainfo'] = extrainfo
        u.flush()
        MSDatabase.getInstance().users.add(u)
        log.info('registrated user %s as %s', email, MSUser.hash(email))
        return ['success']

    def modify(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        if MSUser.isValid(cookie):
            u = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if u != None and u.validate(cookie):
                cardtype    = form.getvalue('user_cardtype')
                realname    = form.getvalue('user_realname')
                phone       = form.getvalue('user_phone')
                civility    = form.getvalue('user_civility')
                startdate   = form.getvalue('user_startdate')
                stopdate    = form.getvalue('user_stopdate')
                photolink   = form.getvalue('user_photolink')
                extrainfo   = form.getvalue('user_extrainfo')
                mapShow     = form.getvalue('user_displayAtMap')
                mapComment  = form.getvalue('user_mapComment')

                if (not startdate) or (not stopdate): return ['Ошибка - введите дату выдачи и оконачания карты Hot-Line в требуемом формате']
                try:
                    if (not startdate) or (not stopdate): raise RuntimeError('argument missing')
                    startdate = datetime.datetime.strptime(startdate, '%d.%m.%Y')
                    stopdate  = datetime.datetime.strptime(stopdate, '%d.%m.%Y')
                except:
                    return ['Ошибка - введите дату выдачи и оконачания карты Hot-Line в требуемом формате']

                if cardtype:    u.storage.meta['cardtype'] = cardtype
                if realname:    u.storage.meta['realname'] = realname
                if phone:       u.storage.meta['phone'] = phone
                if civility:    u.storage.meta['civility'] = civility
                if startdate:   u.storage.meta['startdate'] = startdate
                if stopdate:    u.storage.meta['stopdate'] = stopdate
                if photolink:   u.storage.meta['photolink'] = photolink
                if extrainfo:   u.storage.meta['extrainfo'] = extrainfo
                if mapShow == 'on':
                    u.storage.meta['mapShow'] = True
                else:
                    u.storage.meta['mapShow'] = False
                if mapComment:  u.storage.meta['mapComment'] = mapComment
                u.flush()
                log.info('modified user %s as %s', u.storage.meta['email'], u.hash)
                return ['Изменения успешно внесены']

        return ['Ошибка изменения персональных данных']


    def user_card(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                meta = user.storage.meta
                ret = ['<h2>Профиль пользователя %s</h2>' % (str(meta['email']))]

                ret += ['<div class="row">']
                ret += ['<div class="span8">']

                '''
                ret += ['<ul id="tab" class="nav nav-tabs">']
                ret += ['<li class="active">']
                ret += ['<a href="#BaseParams" data-toggle="tab">Основые параметры</a>']
                ret += ['</li>'] #закрыли таб
                ret += ['<li>']
                ret += ['<a href="#RuntimeData" data-toggle="tab">Оперативная информация</a>']
                ret += ['</li>'] #закрыли таб
                ret += ['</ul>'] #закрыли навигационный объект

                ret += ['\
                        <div id="myTabContent" class="tab-content">\
                            <div class="tab-pane fade active in" id="BaseParams"><h3>XEP1</h3></div>\
                            <div class="tab-pane fade" id="RuntimeData"><h3>XEP2</h3></div>\
                        </div>\
                        ']
                '''

                ret += ['<form class="form-horizontal">']

                statuses = {'A' : ('Карта активна', 'label-success'), 
                            'D' : ('Карта приостановлена', 'label-warning'),
                            'C' : ('Карта закрыта', 'label-important') }
                if meta.has_key('status'): status = statuses.get(meta['status'])
                else: status = statuses.get('D')
                ret += ['<span class="label %s">%s</span>' % (status[1], status[0])]

                ret += ['<fieldset>']
                textbox = w.PatternElement('user_email', 'span', 'Логин')
                val     = textbox.open({
                                        'class':'input-xlarge uneditable-input',
                                        'name':'user_email',
                                        'id':'user_email',
                                        'size':'30',
                                        'type':'text',
                                       },
                                       meta['email'], True)
                ret += val

                if meta.has_key('cardtype'):
                    textbox = w.PatternElement('user_cardtype', 'span', 'Тип карты')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_cardtype',
                                            'id':'user_cardtype',
                                            'size':'30',
                                            'type':'text',
                                           },
                                           meta['cardtype'], True)
                    ret += val

                if meta.has_key('realname'):
                    textbox = w.PatternElement('user_realname', 'span', 'ФИО')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_realname',
                                            'id':'user_realname',
                                            'size':'30',
                                            'type':'text',
                                           },
                                           meta['realname'], True)
                    ret += val

                if meta.has_key('phone'):
                    phones = meta['phone']
                    phones.strip()
                    phones = phones.split(';')
                    ctr = 1
                    for num in phones:
                        textbox = w.PatternElement('user_phone' + str(ctr), 'span', 'Номер телефона %s' % (str(ctr)))
                        val     = textbox.open({
                                                'class':'input-xlarge uneditable-input',
                                                'name':'user_phone' + str(ctr),
                                                'id':'user_phone' + str(ctr),
                                                'size':'30',
                                                'type':'text',
                                               },
                                               num, True)
                        ret += val
                        ctr += 1

                if meta.has_key('civility'):
                    textbox = w.PatternElement('user_civility', 'span', 'Гражданство')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_civility',
                                            'id':'user_civility',
                                            'size':'30',
                                            'type':'text',
                                           },
                                           meta['civility'], True)
                    ret += val
                if meta.has_key('startdate'):
                    textbox = w.PatternElement('user_startdate_out', 'span', 'Дата выдачи карты Hot-Line')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_startdate_out',
                                            'id':'user_startdate_out',
                                            'size':'30',
                                            'type':'text',
                                           },
                                           meta['startdate'].strftime('%Y-%m-%d'), True)
                    ret += val
                if meta.has_key('stopdate'):
                    shouldHighlightRed = False
                    if meta['stopdate'] - datetime.datetime.today() < datetime.timedelta(days=30): shouldHighlightRed = True
                    if shouldHighlightRed:
                        ret += '<div class="clearfix">\
                                <label class="span8">Внимание! До окончания срока действия меньше 30и дней</label>\
                                </div>'

                    textbox = w.PatternElement('user_stopdate_out', 'span', 'Дата окончания действия карты Hot-Line')
                    val     = textbox.open({
                                            'class':'input-xlarge success uneditable-input',
                                            'name':'user_stopdate_out',
                                            'id':'user_stopdate_out',
                                            'size':'30',
                                            'type':'text',
                                           },
                                           meta['stopdate'].strftime('%Y-%m-%d'), True)
                    ret += val
                if meta.has_key('extrainfo'):
                    textbox = w.PatternElement('user_extrainfo', 'textarea', 'Дополнительная информация')
                    val     = textbox.open({
                                            'class':'input-xlarge',
                                            'name':'user_extrainfo',
                                            'id':'user_extrainfo',
                                            'size':'30',
                                            'type':'text',
                                            'rows':"6"
                                           },
                                           meta['extrainfo'], True)
                    ret += val

                if meta.has_key('mapShow') and meta['mapShow'] == True:
                    textbox = w.PatternElement('user_displayAtMap', 'input', 'Отображать на карте')
                    val     = textbox.open({
                                            'name':'user_displayAtMap',
                                            'id':'user_displayAtMap',
                                            'type':'checkbox',
                                            'checked':'checked',
                                            'disabled':'true'
                                           })
                    ret += val
                else:
                    textbox = w.PatternElement('user_displayAtMap', 'input', 'Отображать на карте')
                    val     = textbox.open({
                                            'name':'user_displayAtMap',
                                            'id':'user_displayAtMap',
                                            'type':'checkbox',
                                            'disabled':'true'
                                           })
                    ret += val


                if meta.has_key('mapComment'):
                    textbox = w.PatternElement('user_mapComment', 'span', 'Комментарий на карте')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_mapComment',
                                            'id':'user_mapComment',
                                            'size':'30',
                                            'type':'text',
                                           },
                                           meta['mapComment'], True)
                    ret += val

                textbox = w.PatternElement('user_batteryState', 'span', 'Заряд аккумулятора')
                val     = textbox.open({
                                        'class':'input-xlarge uneditable-input',
                                        'name':'user_batteryState',
                                        'id':'user_batteryState',
                                        'size':'30',
                                        'type':'text',
                                       },
                                       'информация недоступна', True)
                ret += val

                textbox = w.PatternElement('user_lastAlarm', 'span', 'Последнее тревожное событие')
                val     = textbox.open({
                                        'class':'input-xlarge uneditable-input',
                                        'name':'user_lastAlarm',
                                        'id':'user_lastAlarm',
                                        'size':'30',
                                        'type':'text',
                                       },
                                       'информация недоступна', True)
                ret += val


                ret += ['</fieldset>']
                ret += ['</form>']
                ret += ['</div>'] #закрыли левую область
                ret += ['<div class="span4">']

                ret += ['<div class="clearfix">']
                ret += ['<div id="scale-normal" style="height: 400px">']
                if meta.has_key('photolink'):
                    ret += ['<img src="%s">' % str(meta['photolink'])]
                else:
                    ret += ['<img src="/image/boobs.jpg">']
                ret += ['</div>']
                ret += ['</div>']

                ret += ['</div>'] #закрыли правую область
                ret += ['</div>'] #закрыли весь ряд

                return ret
        return ['please login']

    def user_card_modify(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                meta = user.storage.meta
                ret = ['<h2>Настройки пользователя %s</h2>' % (str(meta['email']))]

                ret += ['<div class="row">']
                if meta.has_key('status') and meta['status'] == 'A':
                    ret += ['<a class="btn" href="/method/user_deactivate" target="_blank">Приостановить карту</a>']
                    ret += ['<a class="btn" href="/method/user_close" target="_blank">Закрыть карту</a>']
                else:
                    ret += ['<a class="btn" href="/method/user_activate" target="_blank">Активировать карту</a>']
                ret += ['<a class="btn" href="/html/password_change.html" target="_blank">Изменить пароль</a>']
                ret += ['</div>']

                ret += ['<div class="row">']
                ret += ['<div class="span8">']
                ret += ['<form action="/method/modify" method="post">']
                ret += ['<fieldset>']
                textbox = w.PatternElement('user_email', 'span', 'Логин')
                val     = textbox.open({
                                        'class':'input-xlarge uneditable-input',
                                        'name':'user_email',
                                        'id':'user_email',
                                        'size':'30',
                                        'type':'text',
                                       },
                                       meta['email'], True)
                ret += val

                ph = ''
                if meta.has_key('cardtype'): ph = meta['cardtype']
                elt =   '<div class="clearfix">'
                elt +=  '<label for="user_cardtype">Тип карты</label>'
                elt +=  '<div class="controls">'
                elt +=  '<select id="user_cardtype" name="user_cardtype">'
                elt +=  '   <option>24часа</option>'
                elt +=  '   <option>рабочие часы</option>'
                elt +=  '   <option>другое</option>'
                elt +=  '</select>'
                elt +=  '<span class="help-inline">* - обязательный параметр</span>'
                elt +=  '</div>'
                elt +=  '</div>'
                '''
                textbox = w.PatternElement('user_cardtype', 'input', 'Тип карты')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_cardtype',
                                        'id':'user_cardtype',
                                        'size':'30',
                                        'type':'text',
                                        'value': ph
                                       }, True)
                ret += val
                '''
                ret += elt

                ph = ''
                if meta.has_key('realname'): ph = meta['realname']
                textbox = w.PatternElement('user_realname', 'input', 'ФИО')
                textbox.setHelpInline('* - обязательный параметр')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_realname',
                                        'id':'user_realname',
                                        'size':'30',
                                        'type':'text',
                                        'value': ph
                                       }, True)
                ret += val

                ph = ''
                if meta.has_key('phone'): ph = meta['phone']
                textbox = w.PatternElement('user_phone', 'input', 'Номер телефона')
                textbox.setHelpInline('* - обязательный параметр')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_phone',
                                        'id':'user_phone',
                                        'size':'30',
                                        'type':'text',
                                        'value':ph
                                       }, True)
                ret += val

                ph = ''
                if meta.has_key('civility'): ph = meta['civility']
                textbox = w.PatternElement('user_civility', 'input', 'Гражданство')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_civility',
                                        'id':'user_civility',
                                        'size':'30',
                                        'type':'text',
                                        'value':ph
                                        }, True)
                ret += val

                ph = ''
                if meta.has_key('startdate'): ph = meta['startdate'].strftime('%Y-%m-%d')
                textbox = w.PatternElement('user_startdate', 'input', 'Дата выдачи карты Hot-Line')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_startdate',
                                        'id':'user_startdate',
                                        'size':'30',
                                        'type':'text',
                                        'value': ph
                                        }, True)
                ret += val

                ph = ''
                if meta.has_key('stopdate'): ph = meta['stopdate'].strftime('%Y-%m-%d')
                textbox = w.PatternElement('user_stopdate', 'input', 'Дата окончания действия карты Hot-Line')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_stopdate',
                                        'id':'user_stopdate',
                                        'size':'30',
                                        'type':'text',
                                        'value': ph
                                        }, True)
                ret += val

                ph = ''
                if meta.has_key('photolink'): ph = meta['photolink']
                textbox = w.PatternElement('user_photolink', 'input', 'Путь к фотографии')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_photolink',
                                        'id':'user_photolink',
                                        'size':'30',
                                        'type':'text',
                                        'value': ph
                                        }, True)
                ret += val

                ph = ''
                if meta.has_key('extrainfo'): ph = meta['extrainfo']
                textbox = w.PatternElement('user_extrainfo', 'textarea', 'Дополнительная информация')
                val     = textbox.open({
                                        'class':'input-xlarge',
                                        'name':'user_extrainfo',
                                        'id':'user_extrainfo',
                                        'size':'30',
                                        'type':'text',
                                       },
                                       ph, True)
                ret += val

                if meta.has_key('mapShow') and meta['mapShow'] == True:
                    textbox = w.PatternElement('user_displayAtMap', 'input', 'Отображать на карте')
                    val     = textbox.open({
                                            'name':'user_displayAtMap',
                                            'id':'user_displayAtMap',
                                            'type':'checkbox',
                                            'checked':'checked',
                                           })
                    ret += val
                else:
                    textbox = w.PatternElement('user_displayAtMap', 'input', 'Отображать на карте')
                    val     = textbox.open({
                                            'name':'user_displayAtMap',
                                            'id':'user_displayAtMap',
                                            'type':'checkbox',
                                           })
                    ret += val

                ph = ''
                if meta.has_key('mapComment'): ph = meta['mapComment']
                textbox = w.PatternElement('user_mapComment', 'input', 'Комментарий на карте')
                val     = textbox.open({
                                        'class':'input-xlarge',
                                        'name':'user_mapComment',
                                        'id':'user_mapComment',
                                        'size':'30',
                                        'maxlength':'6',
                                        'type':'text',
                                        'value': ph
                                       }, True)
                ret += val


                ret += ['</fieldset>']
                ret += ['<button class="btn primary" type="submit">Сохранить изменения</button>']
                ret += ['</form>']
                ret += ['</div>'] #закрыли левую область
                ret += ['</div>'] #закрыли весь ряд

                return ret
        return ['please login']

    def user_activate(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                user.lock.acquire()
                try:
                    user.storage.meta['status'] = 'A'
                    user.flush(True)
                finally:
                    user.lock.release()
                return ['Активирована']
        return ['please login']
    def user_deactivate(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                user.lock.acquire()
                try:
                    user.storage.meta['status'] = 'D'
                    user.flush(True)
                finally:
                    user.lock.release()
                return ['Приостановлена']
        return ['please login']
    def user_close(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                user.lock.acquire()
                try:
                    user.storage.meta['status'] = 'C'
                    user.flush(True)
                finally:
                    user.lock.release()
                return ['Закрыта']
        return ['please login']

    def user_pwd_change(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])

        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        oldpwd  = form.getvalue('user_oldpwd')
        if not Helpers.checkName(oldpwd): return ['Ошибка']
        pwd     = form.getvalue('user_pwd')
        if not Helpers.checkName(pwd): return ['Ошибка']
        pwd2    = form.getvalue('user_pwd2')
        if not Helpers.checkName(pwd2): return ['Ошибка']
        if MSUser.hash(pwd) != MSUser.hash(pwd2): return ['Ошибка - пароли несовпадают']

        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                if MSUser.hash(oldpwd) == user.storage.meta['pwd']:
                    user.lock.acquire()
                    try:
                        user.storage.meta['pwd'] = MSUser.hash(pwd)
                        user.flush(True)
                    finally:
                        user.lock.release()
                    return ['Успешно изменен']
        return ['Ошибка установки пароля']

    def event_protocol(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form        = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)

        if not MSUser.isValid(cookie): return ['Ошибка доступа']
        user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
        if user == None or (not user.validate(cookie)): return ['Ошибка доступа']

        '''
        we're here when cookie is validated; there're 3 ways to obtain events:
        1. via API - apiSKey is provided
        2. via SQL db - sqldbDeviceId is provided
        3. via MSDatabase (planned) - nothing should be provided
        '''

        reader = None
        if user.storage.meta.has_key('skey'):
            reader = ReaderMSAPI.Reader(c.get('msapi','url'))
        if user.storage.meta.has_key('msId'):
            reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                       c.get('msdb','port'),
                                       c.get('msdb','dbname'),
                                       c.get('msdb','user'),
                                       c.get('msdb','pwd'))

        if not reader:
            return ['Ошибка - не предоставлен обязательный параметр']

        startdate   = form.getvalue('obj_startdate')
        if startdate == None:
            startdate = MSDatetime.getToday()[0]
        else:
            startdate = datetime.datetime.strptime(startdate, MSDatetime.TIME_FORMAT_MAGICDB_ORIG)

        if reader != None:
            obj = MSObject.MSObject(c.get('main', 'dbpath'), 'nonwriteable_object')
            obj.open()
            if obj.storage.txn > 0:
                raise RuntimeError('event_protocol: nonwriteable_object exists!')
            if user.storage.meta.has_key('skey'):
                obj.storage.meta['skey'] = user.storage.meta['skey']
            if user.storage.meta.has_key('msId'):
                obj.storage.meta['msId'] = user.storage.meta['msId']
            reader.getEvents_TIMED(obj, startdate)

        ret = []
        ret += ['<h2>']
        ret += ['Протокол событий пользователя "' + str(user.storage.meta['email']) + '" с ' + startdate.strftime(MSDatetime.TIME_FORMAT_MAGICDB_ORIG)]
        ret += ['</h2>']

        t = w.PatternTable('zebra-striped', 'eventTableSortable', 'eventTableBody')

        copied_events = list(obj.events)
        copied_events.reverse()
        ret += t.open([('time', {'class':'header'}),
                       ('message',  {'class':'yellow header'}),
                       ('id',   {'class':'yellow header'}),
                       ('comment',   {'class':'blue header'}),
                      ])
        eventnum = 0
        cursor = reader.getCursor()
        for e in copied_events:
            code = MSEvent.eventType(e.contactIdFull, 't') + ' ' + MSEvent.eventCode(e.contactIdFull, 't')
            if code.find('Unknown') != -1: continue

            query   = 'SELECT eventId,comment FROM _comment_data_internal WHERE eventId=%s' % str(e.eventId)
            cursor.execute(query)
            data    = cursor.fetchall()
            if len(data) == 1 and len(data[0][1]) > 0:
                e.eventComment = str(data[0][1])
            else:
                e.eventComment = ''

            ret += t.add((  str(e.eventGenerateTime), 
                            code,
                            str(e.eventId), 
                            str(e.eventComment) ))
            eventnum += 1
        ret += t.close()

        ret += ['Всего событий: <div class="eventTableSize">%d/%d</div><br>' % (eventnum, len(obj.events))]
        ret += ['Время обновления: ']
        ret += ['<div class="eventTableUpdTime">' + (datetime.datetime.fromtimestamp(time.time())).strftime(MSDatetime.TIME_FORMAT_MAGICDB_ORIG) + '</div><br>']
        return ret

    def location_static(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form        = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)

        if not MSUser.isValid(cookie): return ['Ошибка доступа']
        user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
        if user == None or (not user.validate(cookie)): return ['Ошибка доступа']

        '''
        we're here when cookie is validated; there're 3 ways to obtain events:
        1. via API - apiSKey is provided
        2. via SQL db - sqldbDeviceId is provided
        3. via MSDatabase (planned) - nothing should be provided
        '''
        reader = None
        if user.storage.meta.has_key('skey'):
            reader = ReaderMSAPI.Reader(c.get('msapi','url'))
        if user.storage.meta.has_key('msId'):
            reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                       c.get('msdb','port'),
                                       c.get('msdb','dbname'),
                                       c.get('msdb','user'),
                                       c.get('msdb','pwd'))
        if not reader:
            return ['Ошибка - не предоставлен обязательный параметр']

        timerange = MSDatetime.getToday()
        startdate   = form.getvalue('obj_startdate')
        if startdate != None:
            timerange = (datetime.datetime.strptime(startdate, MSDatetime.TIME_FORMAT_MAGICDB_ORIG), timerange[1])

        if reader != None:
            obj = MSObject.MSObject(c.get('main', 'dbpath'), 'nonwriteable_object')
            obj.open()
            if obj.storage.txn > 0:
                raise RuntimeError('event_protocol: nonwriteable_object exists!')
            if user.storage.meta.has_key('skey'):
                obj.storage.meta['skey'] = user.storage.meta['skey']
            if user.storage.meta.has_key('msId'):
                obj.storage.meta['msId'] = user.storage.meta['msId']
            reader.getEvents_GPS_TIMED(obj, reader.normalizeTimerange(timerange))

        ret = []
        ret += ['<h2>']
        ret += ['Местоположение пользователя "' + str(user.storage.meta['email']) + '"']
        ret += ['</h2>']

        copied_events = list(obj.events)
        if len(copied_events) == 0:
            ret += ['нет данных']
            return ret

        ret += ['<script type="text/javascript">']
        ret += ['\n\
                function mapMobileObjects(map) {\n\
                    var blueIcon = new GIcon(G_DEFAULT_ICON);\n\
                    blueIcon.iconSize = new GSize(45,30);\n\
                    blueIcon.image = "http://extras.inyork.com/redesign2010/images/autoIcon.png";\n\
                    markerOptions = { icon:blueIcon };\n\
        ']
        last = copied_events[0]
        ret += ['map.addOverlay(new GMarker(new GLatLng(%s, %s), markerOptions));\n' % (last.GPSLatitudeOrig, last.GPSLongitudeOrig)]
        ret += ['}']

        ret += ['\
                function initialize() {\n\
                  if (GBrowserIsCompatible()) {\n\
                    var map = new GMap2(document.getElementById("map_canvas"));\n\
                    map.setCenter(new GLatLng(59.986379, 30.248780), 10);\n\
                    map.setUIToDefault();\n\
                    mapMobileObjects(map);\n\
                    //var trafficOptions = {incidents:true};\n\
                    //trafficInfo = new GTrafficOverlay(trafficOptions);\n\
                    //map.addOverlay(trafficInfo);\n\
                  }\n\
                }\n']
        ret += ['</script>']
        ret += ['<div id="map_canvas" style="width: 1000px; height: 600px"></div>']
        return ret

    def comment_alarm(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        eventid = form.getvalue('event_id')
        if not eventid:
            return ['Ошибка доступа - не указан номер события']
        try:
            eventid = int(eventid)
        except:
            return ['Ошибка доступа - неверный номер события']

        reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                   c.get('msdb','port'),
                                   c.get('msdb','dbname'),
                                   c.get('msdb','user'),
                                   c.get('msdb','pwd'))
        if not reader:
            return ['Ошибка - пользователь еще не зарегистрирован - нельзя обрабатывать тревоги по нему']

        comment = form.getvalue('event_comment')
        if not comment or len(comment) == 0:
            return ['Ошибка - не задан комментарий']

        cursor  = reader.getCursor()
        query   = 'DELETE FROM _comment_data_internal WHERE eventId=%s' % (str(eventid))
        cursor.execute(query)
        query   = "INSERT INTO _comment_data_internal (eventId, comment) VALUES(%s,'%s')" % (str(eventid), str(comment))
        cursor.execute(query)
        reader.connection.commit()
        return ['Комментарий успешно обновлен']

    def process_alarm_form(self, environ, start_response, cookie):
        loginInfo = self.login(environ, start_response, cookie)
        if len(loginInfo) == 0 or loginInfo[0].find('logged in') == -1:
            return ['Ошибка доступа - неверный логин']
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        eventid = form.getvalue('eventId')
        if not eventid:
            return ['Ошибка доступа - не указан номер события']
        try:
            eventid = int(eventid)
        except:
            return ['Ошибка доступа - неверный номер события']

        user = MSDatabase.getInstance().users.find(MSUser.hash(form.getvalue('login')))
        reader = None
        if user.storage.meta.has_key('msId'):
            reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                       c.get('msdb','port'),
                                       c.get('msdb','dbname'),
                                       c.get('msdb','user'),
                                       c.get('msdb','pwd'))
        if not reader:
            return ['Ошибка - пользователь еще не зарегистрирован - нельзя обрабатывать тревоги по нему']
        cursor  = reader.getCursor()
        query   = 'SELECT deviceId,eventTime,type,id FROM _event_data_internal WHERE id=%s' % str(eventid)
        cursor.execute(query)
        data    = cursor.fetchall()
        events  = ReaderMSDB.createEvents(data, 0)
        if len(events) != 1:
            return ['Ошибка - искомое событие не найдено в базе']
        e       = events[0]

        query   = 'SELECT eventId,comment FROM _comment_data_internal WHERE eventId=%s' % str(eventid)
        cursor.execute(query)
        data    = cursor.fetchall()
        if len(data) == 1 and len(data[0][1]) > 0:
            e.eventComment = str(data[0][1])
        else:
            e.eventComment = ''

        ret = ['<h2>Обработка тревоги %s</h2>' % (str(eventid))]
        t = w.PatternTable('zebra-striped', 'eventTableSortable', 'eventTableBody')
        ret += t.open([('time', {'class':'header'}),
                       ('message',  {'class':'yellow header'}),
                       ('id',   {'class':'yellow header'}),
                       ('comment', {'class':'blue header'}),
                      ])
        code = MSEvent.eventType(e.contactIdFull, 't') + ' ' + MSEvent.eventCode(e.contactIdFull, 't')
        ret += t.add(( str(e.eventGenerateTime),
                       code,
                       str(e.eventId),
                       str(e.eventComment) ))
        ret += t.close()

        ret += ['<div class="row">']
        ret += ['<div class="span8">']
        ret += ['<form action="/method/comment_alarm" method="get">']
        ret += ['<fieldset>']

        textbox = w.PatternElement('event_id', 'input', 'Номер события')
        val     = textbox.open({
                                'class':'input-xlarge uneditable-input',
                                'name':'event_id',
                                'id':'event_id',
                                'size':'30',
                                'type':'text',
                                'value':str(eventid),
                               },
                               True)
        ret += val

        textbox = w.PatternElement('event_comment', 'textarea', 'Комментарий к тревоге')
        val     = textbox.open({
                                'class':'input-xlarge',
                                'name':'event_comment',
                                'id':'event_comment',
                                'size':'30',
                                'type':'text',
                               },
                               e.eventComment, True)
        ret += val
        ret += ['</fieldset>']
        ret += ['<button class="btn primary" type="submit">Сохранить комментарий</button>']
        ret += ['</form>']
        ret += ['</div>'] #закрыли левую область
        ret += ['</div>'] #закрыли весь ряд

        return ret
